package com.atguigu.tingshu.order.service.impl;

import com.alibaba.fastjson.JSON;
import com.atguigu.tingshu.account.client.UserAccountFeignClient;
import com.atguigu.tingshu.album.client.AlbumInfoFeignClient;
import com.atguigu.tingshu.album.client.TrackInfoFeignClient;
import com.atguigu.tingshu.common.constant.KafkaConstant;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.result.Result;
import com.atguigu.tingshu.common.result.ResultCodeEnum;
import com.atguigu.tingshu.common.service.KafkaService;
import com.atguigu.tingshu.model.album.AlbumInfo;
import com.atguigu.tingshu.model.album.TrackInfo;
import com.atguigu.tingshu.model.order.OrderDerate;
import com.atguigu.tingshu.model.order.OrderDetail;
import com.atguigu.tingshu.model.order.OrderInfo;
import com.atguigu.tingshu.model.user.VipServiceConfig;
import com.atguigu.tingshu.order.helper.SignHelper;
import com.atguigu.tingshu.order.mapper.OrderDerateMapper;
import com.atguigu.tingshu.order.mapper.OrderDetailMapper;
import com.atguigu.tingshu.order.mapper.OrderInfoMapper;
import com.atguigu.tingshu.order.service.OrderInfoService;
import com.atguigu.tingshu.user.client.UserInfoFeignClient;
import com.atguigu.tingshu.user.client.VipServiceConfigFeignClient;
import com.atguigu.tingshu.vo.account.AccountLockResultVo;
import com.atguigu.tingshu.vo.account.AccountLockVo;
import com.atguigu.tingshu.vo.order.OrderDerateVo;
import com.atguigu.tingshu.vo.order.OrderDetailVo;
import com.atguigu.tingshu.vo.order.OrderInfoVo;
import com.atguigu.tingshu.vo.order.TradeVo;
import com.atguigu.tingshu.vo.user.UserInfoVo;
import com.atguigu.tingshu.vo.user.UserPaidRecordVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RBlockingQueue;
import org.redisson.api.RDelayedQueue;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Service
@SuppressWarnings({"unchecked", "rawtypes"})
public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo> implements OrderInfoService {

    @Autowired
    private OrderInfoMapper orderInfoMapper;

    @Autowired
    private UserInfoFeignClient userInfoFeignClient;

    @Autowired
    private AlbumInfoFeignClient albumInfoFeignClient;

    @Autowired
    private UserAccountFeignClient userAccountFeignClient;

    @Autowired
    private VipServiceConfigFeignClient vipServiceConfigFeignClient;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private TrackInfoFeignClient trackInfoFeignClient;

    @Autowired
    private KafkaService kafkaService;

    @Autowired
    private OrderDetailMapper orderDetailMapper;

    @Autowired
    private OrderDerateMapper orderDerateMapper;

    @Autowired
    private RedissonClient redissonClient;




    @Override
    public void orderCancel(long orderId) {
        //  本质：修改字段
        OrderInfo orderInfo = new OrderInfo();
        orderInfo.setId(orderId);
        orderInfo.setOrderStatus(SystemConstant.ORDER_STATUS_CANCEL);
        this.updateById(orderInfo);
    }

    @Override
    @GlobalTransactional(rollbackFor = Exception.class)
    public String submitOrder(OrderInfoVo orderInfoVo, Long userId) {
        //  1.  校验签名：
        Map map = JSON.parseObject(JSON.toJSONString(orderInfoVo), Map.class);
        //  为了与订单结算页中payWay 数值保存一致。
        map.put("payWay","");
        SignHelper.checkSign(map);
        //  2.  防止重复回退提交订单.
        //  2.1 先获取到缓存的流水号：
        String tradeNoKey = "user:trade:" + userId;
        //  String tradeNoRedis = (String) this.redisTemplate.opsForValue().get(tradeNoKey);
        //  2.2 页面的流水号
        String tradeNo = orderInfoVo.getTradeNo();
        //  2.3 比较流水号 -- 单线程
        //        if (!tradeNo.equals(tradeNoRedis)){
        //            throw new GuiguException(ResultCodeEnum.ORDER_SUBMIT_REPEAT);
        //        }
        //  2.4 删除流水号  -- 单线程
        //  this.redisTemplate.delete(tradeNoKey);
        //  使用redis-lua脚本删除.
        //  定义一个lua脚本 key-value 键值对匹配成功时，才删除。
        String scriptText = "if redis.call(\"get\",KEYS[1]) == ARGV[1]\n" +
                "then\n" +
                "    return redis.call(\"del\",KEYS[1])\n" +
                "else\n" +
                "    return 0\n" +
                "end";
        //  调用方法.
        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
        redisScript.setScriptText(scriptText);
        redisScript.setResultType(Long.class);
        //  第一个参数lua脚本，第二个参数 , 第三个参数key对应的value.
        Long count = (Long) this.redisTemplate.execute(redisScript, Arrays.asList(tradeNoKey), tradeNo);
        //  判断
        if (count==0){
            throw new GuiguException(ResultCodeEnum.ORDER_SUBMIT_REPEAT);
        }

        //  声明一个orderNo
        String orderNo = UUID.randomUUID().toString().replaceAll("-","");
        //  判断支付类型：
        if (orderInfoVo.getPayWay().equals(SystemConstant.ORDER_PAY_WAY_WEIXIN)){
            //  微信 -- 将数据存储到表中. order_info order_detail order_derate
            this.saveOrder(orderInfoVo,userId,orderNo);
            //  返回订单编号
            return orderNo;
        } else if (orderInfoVo.getPayWay().equals(SystemConstant.ORDER_PAY_WAY_ALIPAY)){
            //  支付宝 - 不用操作
        } else {
            try {
                //  余额
                //  检查与锁定可用余额：tingshu_account.user_account 表；
                AccountLockVo accountDeductVo = new AccountLockVo();
                //  赋值：
                accountDeductVo.setOrderNo(orderNo);
                //  70.00
                accountDeductVo.setAmount(orderInfoVo.getOrderAmount());
                accountDeductVo.setUserId(userId);
                accountDeductVo.setContent("锁定余额支付:"+orderInfoVo.getOrderAmount());
                //  远程调用{同步}, 还可以异步发送消息.{解耦-不能保证强制一致性}
                //  可以使用阿里的seata 分布式事务：
                //  Result<AccountLockResultVo> result = userAccountFeignClient.checkAndLock(accountLockVo);

                Result result = userAccountFeignClient.checkAndDeduct(accountDeductVo);
                //  判断检查与锁定结果.
                if (result.getCode().intValue()!=200){
                    //  检索与锁定失败.
                    throw new GuiguException(result.getCode(),result.getMessage());
                }
                //  保存订单： 将数据存储到表中. order_info order_detail order_derate
                OrderInfo orderInfo = this.saveOrder(orderInfoVo, userId, orderNo);

                // 4.2 TODO 虚拟物品发货-远程调用用户服务新增购买记录
                UserPaidRecordVo userPaidRecordVo = new UserPaidRecordVo();
                userPaidRecordVo.setOrderNo(orderInfo.getOrderNo());
//                userPaidRecordVo.setOrderNo(orderInfoVo.getTradeNo());
                userPaidRecordVo.setUserId(userId);
                //  userPaidRecordVo.setItemType(orderInfo.getItemType());
                userPaidRecordVo.setItemType(orderInfoVo.getItemType());
                List<Long> itemIdList = orderInfoVo.getOrderDetailVoList().stream().map(OrderDetailVo::getItemId).collect(Collectors.toList());
                userPaidRecordVo.setItemIdList(itemIdList);

                Result paidRecordResult = userInfoFeignClient.savePaidRecord(userPaidRecordVo);
                if (200 != paidRecordResult.getCode()) {
                    //新增购买记录失败：全局事务都需要回滚
                    throw new GuiguException(211, "新增购买记录异常");
                }
                //  支付成功扣减金额 -- 异步操作：需要保证数据最终一致性!
                //  kafkaService.sendMsg(KafkaConstant.QUEUE_ACCOUNT_MINUS, orderNo);
                //  返回订单编号
                return orderNo;
            } catch (GuiguException e) {
                log.error("立即结算失败:"+e.getMessage());
                //  解锁金额：
                //  kafkaService.sendMsg(KafkaConstant.QUEUE_ACCOUNT_UNLOCK, orderNo);
                //  e.printStackTrace();
                throw new RuntimeException(e);
            }
        }
        //  返回订单编号
        return orderNo;
    }

    /**
     * 保存订单： order_info order_detail order_derate
     * @param orderInfoVo
     * @param userId
     * @param orderNo
     */
    @Transactional(rollbackFor = Exception.class)
    public OrderInfo saveOrder(OrderInfoVo orderInfoVo, Long userId, String orderNo) {
        //  order_info
        OrderInfo orderInfo = new OrderInfo();
        //  赋值：
        BeanUtils.copyProperties(orderInfoVo,orderInfo);
        //  赋值用户Id
        orderInfo.setUserId(userId);
        //  赋值订单标题
        orderInfo.setOrderTitle(orderInfoVo.getOrderDetailVoList().get(0).getItemName());
        //  订单编号
        orderInfo.setOrderNo(orderNo);
        //  订单状态
        orderInfo.setOrderStatus(SystemConstant.ORDER_STATUS_UNPAID);
        //  保存数据：
        orderInfoMapper.insert(orderInfo);
        //  order_detail
        //  先获取到订单明细数据. 48242 --> 48243 -->
        List<OrderDetailVo> orderDetailVoList = orderInfoVo.getOrderDetailVoList();
        //  判断
        if (!CollectionUtils.isEmpty(orderDetailVoList)){
            //  循环遍历.
            orderDetailVoList.forEach(orderDetailVo -> {
                //  创建订单明细对象
                OrderDetail orderDetail = new OrderDetail();
                //  属性拷贝：
                BeanUtils.copyProperties(orderDetailVo,orderDetail);
                //  赋值订单Id 属性
                orderDetail.setOrderId(orderInfo.getId());
                //  执行n条insert 语句.
                orderDetailMapper.insert(orderDetail);
            });
        }
        //  先获取到减免明细集合 order_derate
        List<OrderDerateVo> orderDerateVoList = orderInfoVo.getOrderDerateVoList();
        //  判断
        if (!CollectionUtils.isEmpty(orderDerateVoList)){
            //  循环遍历.
            orderDerateVoList.forEach(orderDerateVo -> {
                //  创建对象order_derate
                OrderDerate orderDerate = new OrderDerate();
                //  赋值：
                BeanUtils.copyProperties(orderDerateVo,orderDerate);
                //  赋值订单Id
                orderDerate.setOrderId(orderInfo.getId());
                //  保存
                orderDerateMapper.insert(orderDerate);
            });
        }
        //  判断支付方式：
        if (orderInfoVo.getPayWay().equals(SystemConstant.ORDER_PAY_ACCOUNT)){
            //  账户余额 --- 记录用户购买记录.
            //  orderNo = 随机生成的订单编号
            this.orderPaySuccess(orderNo);
        } else {
            //  微信 -- 发送延迟消息. kafka 不能发送延迟消息. redissconClient.
            //  订单表的主键.
            this.sendDelayMessage(orderInfo.getId());
        }
        return orderInfo;
    }

    /**
     * 发送延迟消息.
     * @param orderId
     */
    private void sendDelayMessage(Long orderId) {
        //  发送消息.
        try {
            //  借助 redissconClient
            RBlockingQueue<Object> blockingQueue = redissonClient.getBlockingQueue(KafkaConstant.QUEUE_ORDER_CANCEL);
            //  获取到延迟队列
            RDelayedQueue<Object> delayedQueue = redissonClient.getDelayedQueue(blockingQueue);
            //  延迟5分钟.
            delayedQueue.offer(orderId.toString(),KafkaConstant.DELAY_TIME,TimeUnit.SECONDS);
            log.info("添加延时队列成功 ，延迟时间：{}，订单id：{}", KafkaConstant.DELAY_TIME, orderId);
        } catch (Exception e) {
            log.error("添加延时队列失败 ，延迟时间：{}，订单id：{}", KafkaConstant.DELAY_TIME, orderId);
            e.printStackTrace();
        }

    }

    /**
     * 保存用户信息
     * @param orderNo
     */
    @Override
    public void orderPaySuccess(String orderNo) {
        //  更新订单状态.
        //  orderInfo : 要更新的尼尔： wrapper：表示设置更新条件.
        //  update order_info set order_status = '0902' where order_no = ?;
        OrderInfo orderInfoUpd = new OrderInfo();
        orderInfoUpd.setOrderStatus(SystemConstant.ORDER_STATUS_PAID);
        LambdaQueryWrapper<OrderInfo> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(OrderInfo::getOrderNo,orderNo);
        this.orderInfoMapper.update(orderInfoUpd,wrapper);
        //  根据订单编号来获取到订单对象.
        OrderInfo orderInfo = this.getOrderInfoByOrderNo(orderNo);
        //  订单明细Id
        List<Long> itemIdList = orderInfo.getOrderDetailList().stream().map(OrderDetail::getItemId).collect(Collectors.toList());
        //  使用余额购买了什么? 记录用户的购买日志: 专辑-声音-vip.
        //  创建一个实体类来记录用户交易信息.
        UserPaidRecordVo userPaidRecordVo = new UserPaidRecordVo();
        //  订单编号
        userPaidRecordVo.setOrderNo(orderNo);
        //  用户Id orderInfo.userId
        userPaidRecordVo.setUserId(orderInfo.getUserId());
        //  下订单的类型：专辑-声音-vip.  orderInfo.itemType
        userPaidRecordVo.setItemType(orderInfo.getItemType());
        //  专辑Id-List<声音Id>-vip.service.config.id.
        //  order_detail.item_id
        userPaidRecordVo.setItemIdList(itemIdList);
        //  将上述对象发送到队列中.
        //  this.kafkaService.sendMsg(KafkaConstant.QUEUE_USER_PAY_RECORD,JSON.toJSONString(userPaidRecordVo));

    }

    @Override
    public IPage<OrderInfo> findUserPage(Page<OrderInfo> orderInfoPage, Long userId, String orderStatus) {
        //  调用mapper 层
        IPage<OrderInfo> orderInfoIpage = orderInfoMapper.selectUserPage(orderInfoPage,userId,orderStatus);
        //  需要查看订单状态名称.
        orderInfoIpage.getRecords().forEach(orderInfo -> {
            //  根据订单状态获取状态名称.
            orderInfo.setOrderStatusName(getOrderStatusName(orderInfo.getOrderStatus()));
        });

        return orderInfoIpage;
    }

    /**
     * 根据状态获取状态名称.
     * @param orderStatus
     * @return
     */
    private String getOrderStatusName(String orderStatus) {
        //  声明一个变量
        String orderName = "";
        if (SystemConstant.ORDER_STATUS_UNPAID.equals(orderStatus)){
            orderName = "未支付";
        } else if (SystemConstant.ORDER_STATUS_PAID.equals(orderStatus)){
            orderName = "已支付";
        } else {
            orderName = "已取消";
        }
        //  返回数据
        return orderName;
    }

    /**
     * 根据订单编号获取到订单对象
     * @param orderNo
     * @return
     */
    @Override
    public OrderInfo getOrderInfoByOrderNo(String orderNo) {
        //  select * from order_info where order_no = ?;
        OrderInfo orderInfo = this.orderInfoMapper.selectOne(new LambdaQueryWrapper<OrderInfo>().eq(OrderInfo::getOrderNo, orderNo));
        //  查询订单明细
        if (null != orderInfo){
            //  赋值订单明细
            orderInfo.setOrderDetailList(orderDetailMapper.selectList(new LambdaQueryWrapper<OrderDetail>().eq(OrderDetail::getOrderId,orderInfo.getId())));
            //  订单减免.
            orderInfo.setOrderDerateList(orderDerateMapper.selectList(new LambdaQueryWrapper<OrderDerate>().eq(OrderDerate::getOrderId,orderInfo.getId())));
        }
        //  获取支付方式：
        String payWayName = this.getPayWayName(orderInfo.getPayWay());
        //  获取支付名称
        orderInfo.setPayWayName(payWayName);
        return orderInfo;
    }

    /**
     * 获取支付方式名称
     * @param payWay
     * @return
     */
    private String getPayWayName(String payWay) {
        //  获取支付方式名称
        return SystemConstant.ORDER_PAY_WAY_WEIXIN.equals(payWay)?"微信":"余额";
    }

    @Override
    public OrderInfoVo trade(TradeVo tradeVo, Long userId) {
        //  声明字段：
        //  原始金额
        BigDecimal originalAmount = new BigDecimal("0.00");
        //  减免金额
        BigDecimal derateAmount = new BigDecimal("0.00");
        //  订单总金额
        BigDecimal orderAmount = new BigDecimal("0.00");
        //  订单明细列表
        List<OrderDetailVo> orderDetailVoList = new ArrayList<>();
        //  订单减免列表
        List<OrderDerateVo> orderDerateVoList = new ArrayList<>();
        //  判断购买的类型
        if (tradeVo.getItemType().equals(SystemConstant.ORDER_ITEM_TYPE_ALBUM)) {
            //  购买专辑
            //  判断用户是否购买过专辑.需要一个远程调用!
            Result<Boolean> isPaidAlbumResult = userInfoFeignClient.isPaidAlbum(tradeVo.getItemId());
            // 判断
            Assert.notNull(isPaidAlbumResult, "用户购买专辑记录返回值为空");
            Boolean result = isPaidAlbumResult.getData();
            //  判断
            if (result) {
                //  购买过
                throw new GuiguException(ResultCodeEnum.REPEAT_BUY_ERROR);
            }
            //  没有购买过，应该计算金额{原始金额，订单金额，减免金额} 需要验证一下用户的身份.
            Result<UserInfoVo> userInfoVoResult = userInfoFeignClient.getUserInfoVo(userId);
            Assert.notNull(userInfoVoResult, "用户结果集为空");
            UserInfoVo userInfoVo = userInfoVoResult.getData();
            Assert.notNull(userInfoVo, "用户对象为空");

            //  获取到专辑信息.
            Result<AlbumInfo> albumInfoResult = albumInfoFeignClient.getAlbumInfo(tradeVo.getItemId());
            //  判断
            Assert.notNull(albumInfoResult, "返回专辑集合为空");
            AlbumInfo albumInfo = albumInfoResult.getData();
            Assert.notNull(albumInfo, "返回专辑为空");
            //  非vip计算
            if (userInfoVo.getIsVip().intValue() == 0) {
                //  原始金额：
                originalAmount = albumInfo.getPrice();
                //  非vip discount 如果等于 -1 不打折
                if (albumInfo.getDiscount().doubleValue() != -1) {
                    //  需要折扣9 1000  1000*(10-9) = 1000/10 = 100：减免金额; 1000-100=900 订单金额;
                    //  获取到减免金额
                    derateAmount = originalAmount.multiply(new BigDecimal("10").subtract(albumInfo.getDiscount())).divide(new BigDecimal("10"), 2, RoundingMode.HALF_UP);
                }
                //  订单金额  1000-100=900 订单金额;
                orderAmount = originalAmount.subtract(derateAmount);
            } else {
                //  vip vip_discount 如果等于 -1 不打折
                //  原始金额：
                originalAmount = albumInfo.getPrice();
                //  非vip discount 如果等于 -1 不打折
                if (albumInfo.getVipDiscount().doubleValue() != -1) {
                    //  需要折扣9 1000  1000*(10-9) = 1000/10 = 100：减免金额; 1000-100=900 订单金额;
                    //  获取到减免金额
                    derateAmount = originalAmount.multiply(new BigDecimal("10").subtract(albumInfo.getVipDiscount())).divide(new BigDecimal("10"), 2, RoundingMode.HALF_UP);
                }
                //  订单金额  1000-100=900 订单金额;
                orderAmount = originalAmount.subtract(derateAmount);
            }

            //  订单明细：
            OrderDetailVo orderDetailVo = new OrderDetailVo();
            orderDetailVo.setItemId(tradeVo.getItemId());
            orderDetailVo.setItemUrl(albumInfo.getCoverUrl());
            orderDetailVo.setItemName(albumInfo.getAlbumTitle());
            orderDetailVo.setItemPrice(orderAmount);
            //  将明细添加到集合中.
            orderDetailVoList.add(orderDetailVo);
            //  判断折扣金额是否大于0
            //  if (derateAmount.compareTo(new BigDecimal("0"))!=0)
            if (derateAmount.compareTo(BigDecimal.ZERO) != 0) {
                //  减免明细：
                OrderDerateVo orderDerateVo = new OrderDerateVo();
                orderDerateVo.setDerateAmount(derateAmount);
                orderDerateVo.setDerateType(SystemConstant.ORDER_DERATE_ALBUM_DISCOUNT);
                orderDerateVo.setRemarks("专辑折扣");
                orderDerateVoList.add(orderDerateVo);
            }

        } else if (tradeVo.getItemType().equals(SystemConstant.ORDER_ITEM_TYPE_TRACK)) {
            //  购买声音
            //  声音Id
            Long trackId = tradeVo.getItemId();
            //  购买集数
            Integer trackCount = tradeVo.getTrackCount();
            if (trackCount < 0) {
                throw new GuiguException(ResultCodeEnum.ARGUMENT_VALID_ERROR);
            }
            //  获取当前声音明细列表：
            Result<List<TrackInfo>> trackInfoListResult = trackInfoFeignClient.findPaidTrackInfoList(trackId, trackCount);
            Assert.notNull(trackInfoListResult, "返回声音结果集为空");
            List<TrackInfo> trackInfoList = trackInfoListResult.getData();
            Assert.notNull(trackInfoList, "声音集合为空");

            //  计算金额:
            //  获取到当前专辑信息
            Result<AlbumInfo> albumInfoResult = albumInfoFeignClient.getAlbumInfo(trackInfoList.get(0).getAlbumId());
            Assert.notNull(albumInfoResult, "返回专辑结果集为空");
            AlbumInfo albumInfo = albumInfoResult.getData();
            Assert.notNull(albumInfo, "专辑为空");

            //  原始金额：单价*数量
            originalAmount = trackCount.intValue() > 0 ? albumInfo.getPrice().multiply(new BigDecimal(String.valueOf(trackCount))) : albumInfo.getPrice();
            //  订单金额：声音不支持折扣.
            orderAmount = originalAmount;

            //  订单明细。
            orderDetailVoList = trackInfoList.stream().map(trackInfo -> {
                //  创建订单明细对象
                OrderDetailVo orderDetailVo = new OrderDetailVo();
                //  赋值：
                orderDetailVo.setItemId(trackInfo.getId());
                orderDetailVo.setItemUrl(trackInfo.getCoverUrl());
                orderDetailVo.setItemName(trackInfo.getTrackTitle());
                orderDetailVo.setItemPrice(albumInfo.getPrice());
                //  返回对象
                return orderDetailVo;
            }).collect(Collectors.toList());

        } else {
            //  购买vip
            //  获取到购买vip服务配置信息. {"itemType":"1003","itemId":2} vip_service_config.id=2
            Result<VipServiceConfig> vipServiceConfigResult = vipServiceConfigFeignClient.getVipServiceConfig(tradeVo.getItemId());
            Assert.notNull(vipServiceConfigResult, "vip服务配置数据为空");
            VipServiceConfig vipServiceConfig = vipServiceConfigResult.getData();
            Assert.notNull(vipServiceConfig, "配置信息为空");

            //  计算原始金额：60
            originalAmount = vipServiceConfig.getPrice();
            //  计算折扣金额
            derateAmount = originalAmount.subtract(vipServiceConfig.getDiscountPrice());
            //  计算订单金额：
            orderAmount = vipServiceConfig.getDiscountPrice();

            //  订单明细：
            OrderDetailVo orderDetailVo = new OrderDetailVo();
            orderDetailVo.setItemId(tradeVo.getItemId());
            orderDetailVo.setItemUrl(vipServiceConfig.getImageUrl());
            orderDetailVo.setItemName(vipServiceConfig.getName());
            orderDetailVo.setItemPrice(orderAmount);
            //  将明细添加到集合中.
            orderDetailVoList.add(orderDetailVo);
            //  判断折扣金额是否大于0
            //  if (derateAmount.compareTo(new BigDecimal("0"))!=0)
            if (derateAmount.compareTo(BigDecimal.ZERO) != 0) {
                //  减免明细：
                OrderDerateVo orderDerateVo = new OrderDerateVo();
                orderDerateVo.setDerateAmount(derateAmount);
                orderDerateVo.setDerateType(SystemConstant.ORDER_DERATE_VIP_SERVICE_DISCOUNT);
                orderDerateVo.setRemarks("vip折扣");
                orderDerateVoList.add(orderDerateVo);
            }
        }

        //  创建一个对象
        OrderInfoVo orderInfoVo = new OrderInfoVo();
        //  生成一个流水号
        String tradeNo = UUID.randomUUID().toString().replaceAll("-", "");
        //  防止用户重复回退无刷新提交订单！
        //  在缓存中存储一个流水号：
        String tradeNoKey = "user:trade:" + userId;
        redisTemplate.opsForValue().set(tradeNoKey, tradeNo, 10, TimeUnit.MINUTES);
        //  给orderInfoVo 赋值
        orderInfoVo.setTradeNo(tradeNo);
        //  支付方式有微信支付，余额支付 可以给个默认或空值
        orderInfoVo.setPayWay("");
        orderInfoVo.setItemType(tradeVo.getItemType());
        orderInfoVo.setOriginalAmount(originalAmount);
        orderInfoVo.setOrderAmount(orderAmount);
        orderInfoVo.setDerateAmount(derateAmount);
        orderInfoVo.setOrderDetailVoList(orderDetailVoList);
        orderInfoVo.setOrderDerateVoList(orderDerateVoList);
        //  时间戳：
        orderInfoVo.setTimestamp(SignHelper.getTimestamp());
        //  防止用户非法篡改订单数据.
        //  1.  orderInfoVo 变为一个map集合
        Map map = JSON.parseObject(JSON.toJSONString(orderInfoVo), Map.class);
        //  2.  调用工具类的方法.
        String sign = SignHelper.getSign(map);
        //  3.  赋值签名
        orderInfoVo.setSign(sign);
        //  返回数据
        return orderInfoVo;
    }
}
